home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / samba.idb / usr / samba / src / source / nmbd_incomingrequests.c.z / nmbd_incomingrequests.c
Encoding:
C/C++ Source or Header  |  1998-10-28  |  20.5 KB  |  607 lines

  1. /* 
  2.    Unix SMB/Netbios implementation.
  3.    Version 1.9.
  4.    NBT netbios routines and daemon - version 2
  5.    Copyright (C) Andrew Tridgell 1994-1998
  6.    Copyright (C) Luke Kenneth Casson Leighton 1994-1998
  7.    Copyright (C) Jeremy Allison 1994-1998
  8.    
  9.    This program is free software; you can redistribute it and/or modify
  10.    it under the terms of the GNU General Public License as published by
  11.    the Free Software Foundation; either version 2 of the License, or
  12.    (at your option) any later version.
  13.    
  14.    This program is distributed in the hope that it will be useful,
  15.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.    GNU General Public License for more details.
  18.    
  19.    You should have received a copy of the GNU General Public License
  20.    along with this program; if not, write to the Free Software
  21.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.    
  23.    This file contains all the code to process NetBIOS requests coming
  24.    in on port 137. It does not deal with the code needed to service
  25.    WINS server requests, but only broadcast and unicast requests.
  26.  
  27. */
  28.  
  29. #include "includes.h"
  30.  
  31. extern int DEBUGLEVEL;
  32. extern fstring myworkgroup;
  33.  
  34. /****************************************************************************
  35. Send a name release response.
  36. **************************************************************************/
  37.  
  38. static void send_name_release_response(int rcode, struct packet_struct *p)
  39. {
  40.   struct nmb_packet *nmb = &p->packet.nmb;
  41.   char rdata[6];
  42.  
  43.   memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
  44.   
  45.   reply_netbios_packet(p,                            /* Packet to reply to. */
  46.                        rcode,                        /* Result code. */
  47.                        NMB_REL,                      /* nmbd type code. */
  48.                        NMB_NAME_RELEASE_OPCODE,      /* opcode. */
  49.                        0,                            /* ttl. */
  50.                        rdata,                        /* data to send. */
  51.                        6);                           /* data length. */
  52. }
  53.  
  54. /****************************************************************************
  55. Process a name release packet on a broadcast subnet.
  56. Ignore it if it's not one of our names.
  57. ****************************************************************************/
  58.  
  59. void process_name_release_request(struct subnet_record *subrec, 
  60.                                   struct packet_struct *p)
  61. {
  62.   struct nmb_packet *nmb = &p->packet.nmb;
  63.   struct in_addr owner_ip;
  64.   struct nmb_name *question = &nmb->question.question_name;
  65.   BOOL bcast = nmb->header.nm_flags.bcast;
  66.   uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
  67.   BOOL group = (nb_flags & NB_GROUP) ? True : False;
  68.   struct name_record *namerec;
  69.   int rcode = 0;
  70.   
  71.   putip((char *)&owner_ip,&nmb->additional->rdata[2]);  
  72.   
  73.   if(!bcast)
  74.   {
  75.     /* We should only get broadcast name release packets here.
  76.        Anyone trying to release unicast should be going to a WINS
  77.        server. If the code gets here, then either we are not a wins
  78.        server and they sent it anyway, or we are a WINS server and
  79.        the request was malformed. Either way, log an error here.
  80.        and send an error reply back.
  81.      */
  82.     DEBUG(0,("process_name_release_request: unicast name release request \
  83. received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
  84.           namestr(question), inet_ntoa(owner_ip), subrec->subnet_name));      
  85.  
  86.     send_name_release_response(FMT_ERR, p);
  87.     return;
  88.   }
  89.  
  90.   DEBUG(3,("process_name_release_request: Name release on name %s, \
  91. subnet %s from owner IP %s\n",
  92.            namestr(&nmb->question.question_name),
  93.            subrec->subnet_name, inet_ntoa(owner_ip)));
  94.   
  95.   /* If someone is releasing a broadcast group name, just ignore it. */
  96.   if( group && !ismyip(owner_ip) )
  97.     return;
  98.  
  99.   /*
  100.    * Code to work around a bug in FTP OnNet software NBT implementation.
  101.    * They do a broadcast name release for WORKGROUP<0> and WORKGROUP<1e>
  102.    * names and *don't set the group bit* !!!!!
  103.    */
  104.  
  105.   if( !group && !ismyip(owner_ip) && strequal(question->name, myworkgroup) && 
  106.       ((question->name_type == 0x0) || (question->name_type == 0x1e)))
  107.   {
  108.     DEBUG(6,("process_name_release_request: FTP OnNet bug workaround. Ignoring \
  109. group release name %s from IP %s on subnet %s with no group bit set.\n",
  110.         namestr(question), inet_ntoa(owner_ip), subrec->subnet_name ));
  111.     return;
  112.   }
  113.  
  114.   namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_ANY_NAME);
  115.  
  116.   /* We only care about someone trying to release one of our names. */
  117.   if (namerec && ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)))
  118.   {
  119.     rcode = ACT_ERR;
  120.     DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \
  121. on subnet %s being rejected as it is one of our names.\n", 
  122.           namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name));
  123.   }
  124.  
  125.   if(rcode == 0)
  126.     return;
  127.  
  128.   /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
  129.   send_name_release_response(rcode, p);
  130. }
  131.  
  132. /****************************************************************************
  133. Send a name registration response.
  134. **************************************************************************/
  135.  
  136. static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p)
  137. {
  138.   struct nmb_packet *nmb = &p->packet.nmb;
  139.   char rdata[6];
  140.  
  141.   memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
  142.   
  143.   reply_netbios_packet(p,                             /* Packet to reply to. */
  144.                        rcode,                         /* Result code. */
  145.                        NMB_REG,                       /* nmbd type code. */
  146.                        NMB_NAME_REG_OPCODE,           /* opcode. */
  147.                        ttl,                           /* ttl. */
  148.                        rdata,                         /* data to send. */
  149.                        6);                            /* data length. */
  150. }
  151.  
  152. /****************************************************************************
  153. Process a name refresh request on a broadcast subnet.
  154. **************************************************************************/
  155.      
  156. void process_name_refresh_request(struct subnet_record *subrec,
  157.                                   struct packet_struct *p)
  158. {    
  159.      
  160.   struct nmb_packet *nmb = &p->packet.nmb;
  161.   struct nmb_name *question = &nmb->question.question_name;
  162.   BOOL bcast = nmb->header.nm_flags.bcast;
  163.   struct in_addr from_ip;
  164.   
  165.   putip((char *)&from_ip,&nmb->additional->rdata[2]);
  166.  
  167.   if(!bcast)
  168.   { 
  169.     /* We should only get broadcast name refresh packets here.
  170.        Anyone trying to refresh unicast should be going to a WINS
  171.        server. If the code gets here, then either we are not a wins
  172.        server and they sent it anyway, or we are a WINS server and
  173.        the request was malformed. Either way, log an error here.
  174.        and send an error reply back.
  175.      */
  176.     DEBUG(0,("process_name_refresh_request: unicast name registration request \
  177. received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
  178.           namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
  179.     
  180.     send_name_registration_response(FMT_ERR, 0, p);
  181.     return;
  182.   } 
  183.  
  184.   /* Just log a message. We really don't care about broadcast name
  185.      refreshes. */
  186.      
  187.   DEBUG(3,("process_name_refresh_request: Name refresh for name %s \
  188. IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
  189.      
  190. }
  191.     
  192. /****************************************************************************
  193. Process a name registration request on a broadcast subnet.
  194. **************************************************************************/
  195.  
  196. void process_name_registration_request(struct subnet_record *subrec, 
  197.                                        struct packet_struct *p)
  198. {
  199.   struct nmb_packet *nmb = &p->packet.nmb;
  200.   struct nmb_name *question = &nmb->question.question_name;
  201.   BOOL bcast = nmb->header.nm_flags.bcast;
  202.   uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
  203.   BOOL group = (nb_flags & NB_GROUP) ? True : False;
  204.   struct name_record *namerec = NULL;
  205.   int ttl = nmb->additional->ttl;
  206.   struct in_addr from_ip;
  207.   
  208.   putip((char *)&from_ip,&nmb->additional->rdata[2]);
  209.   
  210.   if(!bcast)
  211.   {
  212.     /* We should only get broadcast name registration packets here.
  213.        Anyone trying to register unicast should be going to a WINS
  214.        server. If the code gets here, then either we are not a wins
  215.        server and they sent it anyway, or we are a WINS server and
  216.        the request was malformed. Either way, log an error here.
  217.        and send an error reply back.
  218.      */
  219.     DEBUG(0,("process_name_registration_request: unicast name registration request \
  220. received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
  221.           namestr(question), inet_ntoa(from_ip), subrec->subnet_name));      
  222.  
  223.     send_name_registration_response(FMT_ERR, 0, p);
  224.     return;
  225.   }
  226.  
  227.   DEBUG(3,("process_name_registration_request: Name registration for name %s \
  228. IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
  229.   
  230.   /* See if the name already exists. */
  231.   namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
  232.  
  233.   /* 
  234.    * If the name being registered exists and is a WINS_PROXY_NAME 
  235.    * then delete the WINS proxy name entry so we don't reply erroneously
  236.    * later to queries.
  237.    */
  238.  
  239.   if((namerec != NULL) && (namerec->source == WINS_PROXY_NAME))
  240.   {
  241.     remove_name_from_namelist( subrec, namerec );
  242.     namerec = NULL;
  243.   }
  244.  
  245.   if (!group)
  246.   {
  247.     /* Unique name. */
  248.  
  249.     if ((namerec != NULL) && 
  250.         ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME) ||
  251.           NAME_GROUP(namerec))
  252.        )
  253.     {
  254.       /* No-one can register one of Samba's names, nor can they
  255.          register a name that's a group name as a unique name */
  256.  
  257.       send_name_registration_response(ACT_ERR, 0, p);
  258.       return;
  259.     }
  260.     else if(namerec != NULL)
  261.     {
  262.       /* Update the namelist record with the new information. */
  263.       namerec->ip[0] = from_ip;
  264.       update_name_ttl(namerec, ttl);
  265.  
  266.       DEBUG(3,("process_name_registration_request: Updated name record %s \
  267. with IP %s on subnet %s\n",namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
  268.       return;
  269.     }
  270.   }
  271.   else
  272.   {
  273.     /* Group name. */
  274.  
  275.     if((namerec != NULL) && !NAME_GROUP(namerec) &&
  276.         ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))
  277.       )
  278.     {
  279.       /* Disallow group names when we have a unique name. */
  280.       send_name_registration_response(ACT_ERR, 0, p);  
  281.       return;  
  282.     }  
  283.   }
  284. }
  285.  
  286. /****************************************************************************
  287. This is used to sort names for a name status into a sensible order.
  288. We put our own names first, then in alphabetical order.
  289. **************************************************************************/
  290.  
  291. static int status_compare(char *n1,char *n2)
  292. {
  293.   extern pstring myname;
  294.   int l1,l2,l3;
  295.  
  296.   /* It's a bit tricky because the names are space padded */
  297.   for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ;
  298.   for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ;
  299.   l3 = strlen(myname);
  300.  
  301.   if ((l1==l3) && strncmp(n1,myname,l3) == 0 && 
  302.       (l2!=l3 || strncmp(n2,myname,l3) != 0))
  303.     return -1;
  304.  
  305.   if ((l2==l3) && strncmp(n2,myname,l3) == 0 && 
  306.       (l1!=l3 || strncmp(n1,myname,l3) != 0))
  307.     return 1;
  308.  
  309.   return memcmp(n1,n2,18);
  310. }
  311.  
  312.  
  313. /****************************************************************************
  314.   Process a node status query
  315.   ****************************************************************************/
  316.  
  317. void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p)
  318. {
  319.   struct nmb_packet *nmb = &p->packet.nmb;
  320.   char *qname   = nmb->question.question_name.name;
  321.   int ques_type = nmb->question.question_name.name_type;
  322.   char rdata[MAX_DGRAM_SIZE];
  323.   char *countptr, *buf, *bufend, *buf0;
  324.   int names_added,i;
  325.   struct name_record *namerec;
  326.  
  327.   DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \
  328. subnet %s.\n", namestr(&nmb->question.question_name), inet_ntoa(p->ip),
  329.           subrec->subnet_name));
  330.  
  331.   if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name,
  332.                                     FIND_SELF_NAME)) == 0)
  333.   {
  334.     DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \
  335. subnet %s - name not found.\n", namestr(&nmb->question.question_name),
  336.           inet_ntoa(p->ip), subrec->subnet_name));
  337.  
  338.     return;
  339.   }
  340.  
  341.   /* XXXX hack, we should calculate exactly how many will fit. */
  342.   bufend = &rdata[MAX_DGRAM_SIZE] - 18;
  343.   countptr = buf = rdata;
  344.   buf += 1;
  345.   buf0 = buf;
  346.  
  347.   names_added = 0;
  348.  
  349.   namerec = subrec->namelist;
  350.  
  351.   while (buf < bufend) 
  352.   {
  353.     if ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))
  354.     {
  355.       int name_type = namerec->name.name_type;
  356.       
  357.       if (!strequal(namerec->name.name,"*") &&
  358.           !strequal(namerec->name.name,"__SAMBA__") &&
  359.           (name_type < 0x1b || name_type >= 0x20 || 
  360.            ques_type < 0x1b || ques_type >= 0x20 ||
  361.            strequal(qname, namerec->name.name)))
  362.       {
  363.         /* Start with the name. */
  364.         bzero(buf,18);
  365.         slprintf(buf,17,"%-15.15s",namerec->name.name);
  366.         strupper(buf);
  367.         
  368.         /* Put the name type and netbios flags in the buffer. */
  369.         buf[15] = name_type;
  370.         set_nb_flags(&buf[16],namerec->nb_flags);
  371.         buf[16] |= NB_ACTIVE; /* all our names are active */
  372.  
  373.         buf += 18;
  374.       
  375.         names_added++;
  376.       }
  377.     }
  378.  
  379.     /* Remove duplicate names. */
  380.     qsort(buf0,names_added,18,QSORT_CAST status_compare);
  381.  
  382.     for (i=1;i<names_added;i++)
  383.     {
  384.       if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) 
  385.       {
  386.         names_added--;
  387.         if (names_added == i)
  388.           break;
  389.         memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
  390.         i--;
  391.       }
  392.     }
  393.  
  394.     buf = buf0 + 18*names_added;
  395.  
  396.     namerec = namerec->next;
  397.  
  398.     if (!namerec)
  399.     {
  400.       /* End of the subnet specific name list. Now 
  401.          add the names on the unicast subnet . */
  402.       struct subnet_record *uni_subrec = unicast_subnet;
  403.  
  404.       if (uni_subrec != subrec)
  405.       {
  406.         subrec = uni_subrec;
  407.         namerec = subrec->namelist;
  408.       }
  409.     }
  410.     if (!namerec)
  411.       break;
  412.  
  413.   }
  414.   
  415.   SCVAL(countptr,0,names_added);
  416.   
  417.   /* We don't send any stats as they could be used to attack
  418.      the protocol. */
  419.   bzero(buf,46);
  420.   
  421.   buf += 46;
  422.   
  423.   /* Send a NODE STATUS RESPONSE */
  424.   reply_netbios_packet(p,                            /* Packet to reply to. */
  425.                        0,                            /* Result code. */
  426.                        NMB_STATUS,                   /* nmbd type code. */
  427.                        NMB_NAME_QUERY_OPCODE,        /* opcode. */
  428.                0,                            /* ttl. */
  429.                        rdata,                        /* data to send. */
  430.                        PTR_DIFF(buf,rdata));         /* data length. */
  431. }
  432.  
  433.  
  434. /***************************************************************************
  435. Process a name query.
  436.  
  437. For broadcast name queries:
  438.  
  439.   - Only reply if the query is for one of YOUR names.
  440.   - NEVER send a negative response to a broadcast query.
  441.  
  442. ****************************************************************************/
  443.  
  444. void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p)
  445. {
  446.   struct nmb_packet *nmb = &p->packet.nmb;
  447.   struct nmb_name *question = &nmb->question.question_name;
  448.   int name_type = question->name_type;
  449.   BOOL bcast = nmb->header.nm_flags.bcast;
  450.   int ttl=0;
  451.   int rcode = 0;
  452.   char *prdata = NULL;
  453.   char rdata[6];
  454.   BOOL success = False;
  455.   struct name_record *namerec = NULL;
  456.   int reply_data_len = 0;
  457.   int i;
  458.  
  459.   DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n", 
  460.             inet_ntoa(p->ip), subrec->subnet_name, namestr(question)));
  461.   
  462.   /* Look up the name in the cache - if the request is a broadcast request that
  463.      came from a subnet we don't know about then search all the broadcast subnets
  464.      for a match (as we don't know what interface the request came in on). */
  465.  
  466.   if(subrec == remote_broadcast_subnet)
  467.     namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME);
  468.   else
  469.     namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
  470.  
  471.  
  472.   /* Check if it is a name that expired */
  473.   if (namerec && ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp)))
  474.   {
  475.     DEBUG(5,("process_name_query_request: expired name %s\n", namestr(&namerec->name)));
  476.     namerec = NULL;
  477.   }
  478.  
  479.   if (namerec)
  480.   {
  481.  
  482.     /* 
  483.      * Always respond to unicast queries.
  484.      * Don't respond to broadcast queries unless the query is for
  485.      * a name we own, a Primary Domain Controller name, or a WINS_PROXY 
  486.      * name with type 0 or 0x20. WINS_PROXY names are only ever added
  487.      * into the namelist if we were configured as a WINS proxy.
  488.      */
  489.  
  490.     if (!bcast || 
  491.         (bcast && ((name_type == 0x1b) || (namerec->source == SELF_NAME) || 
  492.         (namerec->source == PERMANENT_NAME) || 
  493.         ((namerec->source == WINS_PROXY_NAME) && ((name_type == 0) || (name_type == 0x20)))))
  494.        )
  495.     {
  496.       
  497.       /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY, 
  498.          or it's a Domain Master type. */
  499.  
  500.       /*
  501.        * If this is a WINS_PROXY_NAME, then ceck that none of the IP 
  502.        * addresses we are returning is on the same broadcast subnet 
  503.        * as the requesting packet. If it is then don't reply as the 
  504.        * actual machine will be replying also and we don't want two 
  505.        * replies to a broadcast query.
  506.        */
  507.  
  508.       if(namerec->source == WINS_PROXY_NAME)
  509.       {
  510.         for( i = 0; i < namerec->num_ips; i++)
  511.         {
  512.           if(same_net( namerec->ip[i], subrec->myip, subrec->mask_ip ))    
  513.           {
  514.             DEBUG(5,("process_name_query_request: name %s is a WINS proxy name and is also \
  515. on the same subnet (%s) as the requestor. Not replying.\n", 
  516.                    namestr(&namerec->name), subrec->subnet_name ));
  517.             return;
  518.           }
  519.         }   
  520.       }     
  521.  
  522.       ttl = (namerec->death_time != PERMANENT_TTL) ?
  523.                      namerec->death_time - p->timestamp : lp_max_ttl();
  524.  
  525.       /* Copy all known ip addresses into the return data. */
  526.       /* Optimise for the common case of one IP address so 
  527.          we don't need a malloc. */
  528.  
  529.       if(namerec->num_ips == 1 )
  530.         prdata = rdata;
  531.       else
  532.       {
  533.         if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL)
  534.         {
  535.           DEBUG(0,("process_name_query_request: malloc fail !\n"));
  536.           return;
  537.         }
  538.       }
  539.  
  540.       for( i = 0; i < namerec->num_ips; i++)
  541.       {
  542.         set_nb_flags(&prdata[i*6],namerec->nb_flags);
  543.         putip((char *)&prdata[2+(i*6)], &namerec->ip[i]);
  544.       }
  545.       reply_data_len = namerec->num_ips * 6;
  546.       success = True;
  547.     }
  548.   }
  549.  
  550.   /*
  551.    * If a machine is broadcasting a name lookup request and we have lp_wins_proxy()
  552.    * set we should initiate a WINS query here. On success we add the resolved name 
  553.    * into our namelist with a type of WINS_PROXY_NAME and then reply to the query.
  554.    */
  555.  
  556.   if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() && 
  557.      bcast && (subrec != remote_broadcast_subnet))
  558.   {
  559.     make_wins_proxy_name_query_request( subrec, p, question );
  560.     return;
  561.   }
  562.  
  563.   if (!success && bcast)
  564.   {
  565.     if((prdata != rdata) && (prdata != NULL))
  566.       free(rdata);
  567.     return; /* Never reply with a negative response to broadcasts. */
  568.   }
  569.  
  570.   /* 
  571.    * Final check. From observation, if a unicast packet is sent
  572.    * to a non-WINS server with the recursion desired bit set
  573.    * then never send a negative response.
  574.    */
  575.  
  576.   if(!success && !bcast && nmb->header.nm_flags.recursion_desired)
  577.   {
  578.     if((prdata != rdata) && (prdata != NULL))
  579.       free(rdata);
  580.     return;
  581.   }
  582.  
  583.   if (success)
  584.   {
  585.       rcode = 0;
  586.       DEBUG(3,("OK\n"));
  587.   }
  588.   else
  589.   {
  590.       rcode = NAM_ERR;
  591.       DEBUG(3,("UNKNOWN\n"));      
  592.   }
  593.  
  594.   /* See rfc1002.txt 4.2.13. */
  595.  
  596.   reply_netbios_packet(p,                              /* Packet to reply to. */
  597.                        rcode,                          /* Result code. */
  598.                        NMB_QUERY,                      /* nmbd type code. */
  599.                        NMB_NAME_QUERY_OPCODE,          /* opcode. */
  600.                        ttl,                            /* ttl. */
  601.                        prdata,                         /* data to send. */
  602.                        reply_data_len);                /* data length. */
  603.  
  604.   if((prdata != rdata) && (prdata != NULL))
  605.     free(prdata);
  606. }
  607.